﻿using System.Text;

namespace BambuBusParser
{
    public class BambubusPacket
    {
        public string Time { get; set; }
        public bool IsShortFrame { get; private set; }
        public int Sequence { get; private set; }
        public ushort Length { get; private set; }
        public byte HeaderCrc { get; private set; }
        public ushort TotalCrc { get; private set; }
        public bool Crc8 { get; private set; }
        public bool Crc16 { get; private set; }
        public byte Flags { get; set; }
        public ushort? SourceAddress { get; private set; }
        public ushort? TargetAddress { get; private set; }
        public byte? ReadNum { get; set; }
        public byte? State { get; set; }
        public byte? FlimentMotion { get; set; }
        public byte? AMSNum { get; set; }
        public Memory<byte> RawData { get; private set; }
        public PackageType Package { get; set; }
        public static ushort BambuBusAddress { get; set; }

        public ushort PacketLen { get; set; }

        public bool WantsResponse { get; set; }

        public bool IsInitiator { get; set; }

        public string AddrFromName => DEVICES.ContainsKey(SourceAddress ?? 0)
            ? DEVICES[SourceAddress ?? 0]
            : $"";

        public string AddrToName => DEVICES.ContainsKey(TargetAddress ?? 0)
            ? DEVICES[TargetAddress ?? 0]
            : $"";

        public string CommandName => COMMANDS.ContainsKey(_printerDataLong.Type)
            ? COMMANDS[_printerDataLong.Type]
            : $"0x{_printerDataLong.Type:X4}";

        private int _totalCrcIndex;
        private int _contentStartIndex;


        private LongPackageData _printerDataLong;

        static readonly Dictionary<int, string> DEVICES = new Dictionary<int, string>
        {
            { 0x0300, "MC 运动控制" }, { 6, "AP 控制上位机" }, { 0x0700, "AMS 大AMS" }, { 0x1200, "AMS Lite" },
            { 0x0800, "TH 工具头" },
            { 0x0900, "AP2 显示上位机" }, { 0x0E00, "AHB AMSHub" }, { 0x0F00, "EXT" }, { 0x1300, "CTC" }
        };

        static readonly Dictionary<ushort, string> COMMANDS = new Dictionary<ushort, string>
        {
            { 0x0101, "ping" },
            { 0x0103, "get_version" },
            { 0x0104, "sync_timestamp" },
            { 0x0106, "mcu_upgrade" },
            { 0x0108, "mcu_hms" },
            { 0x0109, "factory_reset" },
            { 0x0205, "gcode_execute_state" },
            { 0x0206, "gcode_request" }, // no official name for this
            { 0x0209, "mcu_display_message" },
            { 0x020A, "vosync" },
            { 0x020B, "gcode_ctx" },
            { 0x020C, "mc_state" },
            { 0x020F, "link_ams_report" },
            { 0x0211, "ack_tray_info" },
            { 0x0216, "gcode_line_handle" },
            { 0x0217, "ams_mapping" },
            { 0x0218, "ams_tray_info_write_ack" },
            { 0x0219, "ams_user_settings" },
            { 0x021B, "hw_info_voltage" },
            { 0x021C, "link_ams_tray_consumption_ack" },
            { 0x021D, "pack_get_part_info_ack" },
            { 0x0222, "extrusion_result_update" },
            { 0x0224, "fila_ams_get" },
            { 0x0225, "mc_get_skipped_obj_list" },
            { 0x0301, "M971" },
            { 0x0302, "M972" },
            { 0x0305, "M963" },
            { 0x0307, "M969" },
            { 0x0306, "M965_b" },
            { 0x0309, "M967" },
            { 0x030B, "M973" },
            { 0x030E, "M965" },
            { 0x0331, "M976" },
            { 0x0332, "M977" },
            { 0x0333, "M978" },
            { 0x0334, "M981" },
            { 0x0335, "M991" },
            { 0x0351, "M987" },
            { 0x0352, "SENSORCHECK" },
            { 0x0401, "set_module_sn" },
            { 0x0402, "get_module_sn" },
            { 0x0403, "inject_module_key" },
            { 0x0404, "get_inject_status" },
            { 0x0405, "mcu_reboot" },
            { 0x0406, "send_to_amt_core" },
            { 0x0407, "set_module_lifecycle" },
            { 0x0408, "get_module_lifecycle" },
            { 0x040A, "inject_productcode" },
            { 0x040B, "get_productcode" },
        };

        public static byte[] CxxRes =
        {
            0x3D, 0xE0, 0x2C, 0x1A, 0x03,
            0x00, 0x00, 0x00, 0x00,
            0x90, 0xE4
        };

        public BambubusPacket(Memory<byte> rawData)
        {
            Parse(rawData.Span);
            if (!rawData.IsEmpty && Length > 0 && rawData.Length >= Length)
                RawData = rawData.Slice(0, Length);
        }

        private void Parse(Span<byte> rawData)
        {
            if (rawData.IsEmpty || rawData.Length < 5 || rawData[0] != 0x3D)
            {
                return;
            }

            try
            {
                Flags = rawData[1];

                // Frame type
                IsShortFrame = rawData[1] > 0x80;
                Sequence = IsShortFrame ? 0 : BitConverter.ToUInt16(rawData.Slice(42, 2).ToArray());
                // Length (short frame: 1 byte, long frame: 2 bytes)
                Length = IsShortFrame ? rawData[2] : rawData[4];

                // Header CRC8
                HeaderCrc = rawData[IsShortFrame ? 3 : 6];
                Package = PackageType.None;

                Crc8 = ValidateHeaderCrc(rawData);
                if (!Crc8)
                {
                    Length = 0;
                    return;
                }

                _contentStartIndex = IsShortFrame ? 4 : 5;
                // Total CRC16
                _totalCrcIndex = Length - 2;

                if (Length > 0)
                {
                    TotalCrc = BitConverter.ToUInt16(rawData.Slice(_totalCrcIndex, 2).ToArray());

                    Crc16 = ValidateTotalCrc(rawData);
                }

                if (!IsShortFrame)
                {
                    this.Sequence = (ushort)(rawData[2] | (rawData[3] << 8));
                    this.PacketLen = (ushort)(rawData[4] | (rawData[5] << 8));
                }

                this.IsInitiator = (Flags & 0x4) != 0;
                this.WantsResponse = (Flags & 0x1) != 0;
                Package = GetPackageType(rawData);
                if (Package == PackageType.FilamentMotionLong)
                    SendForDxx(rawData, Length);
                else if (Package == PackageType.FilamentMotionShort)
                    SendForCxx(rawData, Length);
                else if (Package == PackageType.LongPackageFilament)
                    SendForLongPackageFilament(rawData, Length);
            }
            catch (Exception)
            {
            }
        }

        private bool ValidateHeaderCrc(Span<byte> rawData)
        {
            var crc = new Crc8(0x39, 0x66, 0x00, false, false);
            crc.Add(rawData.Slice(0, IsShortFrame ? 3 : 6).ToArray());
            var calculatedCrc = crc.Calc();
            return calculatedCrc == HeaderCrc;
        }

        private bool ValidateTotalCrc(Span<byte> rawData)
        {
            var crc = new Crc16(0x1021, 0x913D, 0, false, false);
            crc.Add(rawData.Slice(0, _totalCrcIndex).ToArray());
            var computedCrc = crc.Calc();
            // Verify the last 2 bytes of data against the computed CRC16
            return computedCrc == TotalCrc;
        }

        void SendForLongPackageFilament(Span<byte> rawData, int length)
        {
            FilamentId = Encoding.UTF8.GetString(_printerDataLong.Datas, 19, 8);
            FilamentName = Encoding.UTF8.GetString(_printerDataLong.Datas, 27, 20);
            FilamentColor =
                $"{_printerDataLong.Datas[59]},{_printerDataLong.Datas[60]},{_printerDataLong.Datas[61]},{_printerDataLong.Datas[62]}";
        }

        public string FilamentId { get; set; }
        public string FilamentName { get; set; }
        public string FilamentColor { get; set; }


        public static void BambubusLongPackageAnalysis(byte[] buf, int dataLength, ref LongPackageData data)
        {
            if (buf == null || buf.Length < 13 || dataLength < 15)
            {
                throw new ArgumentException("Invalid buffer or data length.");
            }

            // Parse fields from the buffer
            data.PackageNumber = BitConverter.ToUInt16(buf, 2); // Extract package_number (2 bytes)
            data.PackageLength = BitConverter.ToUInt16(buf, 4); // Extract package_length (2 bytes)
            data.Crc8 = buf[6]; // Extract crc8 (1 byte)
            data.TargetAddress = BitConverter.ToUInt16(buf, 7); // Extract target_address (2 bytes)
            data.SourceAddress = BitConverter.ToUInt16(buf, 9); // Extract source_address (2 bytes)
            data.Type = BitConverter.ToUInt16(buf, 11); // Extract type (2 bytes)

            // Parse the data payload
            var payloadStartIndex = 13;
            data.DataLength = (ushort)(dataLength - 15); // Subtract the header (13 bytes) and CRC16 (2 bytes)
            data.Datas = new byte[data.DataLength];
            Array.Copy(buf, payloadStartIndex, data.Datas, 0, data.DataLength);
        }


        public PackageType GetPackageType(Span<byte> buf)
        {
            // Short packages (buf[1] == 0xC5)
            if (buf[1] == 0xC5)
            {
                return buf[4] switch
                {
                    0x03 => PackageType.FilamentMotionShort,
                    0x04 => PackageType.FilamentMotionLong,
                    0x05 => PackageType.OnlineDetect,
                    0x06 => PackageType.Reqx6,
                    0x07 => PackageType.NfcDetect,
                    0x08 => PackageType.SetFilament,
                    0x20 => PackageType.Heartbeat,
                    _ => PackageType.Etc,
                };
            }
            // Long packages (buf[1] == 0x05)
            else
            {
                BambubusLongPackageAnalysis(buf.Slice(0, Length).ToArray(), Length, ref _printerDataLong);
                TargetAddress = _printerDataLong.TargetAddress;
                if (_printerDataLong.TargetAddress == 0x1200 || _printerDataLong.TargetAddress == 0x0700)
                    BambuBusAddress = _printerDataLong.TargetAddress;
                SourceAddress = _printerDataLong.SourceAddress;

                // Identify the long package type
                return _printerDataLong.Type switch
                {
                    0x21A => PackageType.LongPackageMcOnline,
                    0x211 => PackageType.LongPackageFilament,
                    0x103 or 0x402 => PackageType.LongPackageVersion,
                    _ => PackageType.Etc,
                };
            }

            return PackageType.None;
        }

        void SendForDxx(Span<byte> buf, int length)
        {
            byte filamentFlagOn = 0x00;
            byte filamentFlagNFC = 0x00;
            byte AMSNum = buf[5];
            byte statuFlags = buf[6];
            byte filamentMotionFlag = buf[7];
            byte readNum = buf[9];
            this.ReadNum = readNum;
            this.AMSNum = AMSNum;
            this.FlimentMotion = filamentMotionFlag;
            this.State = statuFlags;

            SetMotion(AMSNum, readNum, statuFlags, filamentMotionFlag);
        }

        void SendForCxx(Span<byte> buf, int length)
        {
            // 设置初始响应包
            CxxRes[1] = (byte)(0xC0 | (packageNum << 3));
            byte AMSNum = buf[5];
            byte statuFlags = buf[6];
            byte readNum = buf[7];
            byte filamentMotionFlag = buf[8];
            this.AMSNum = AMSNum;
            this.State = statuFlags;
            this.FlimentMotion = filamentMotionFlag;
            this.ReadNum = readNum;

            // 调用 set_motion 逻辑 (注释掉了具体的行为)
            // if (!SetMotion(AMSNum, readNum, statuFlags, filamentMotionFlag))
            //     return;

            // 设置响应数据
            //SetMotionResDatas(CxxRes, AMSNum, readNum);

            // 发送数据包并附加CRC校验
            //PackageSendWithCrc(CxxRes, CxxRes.Length);

            // 更新包编号
            if (packageNum < 7)
                packageNum++;
            else
                packageNum = 0;
        }

        public static int packageNum { get; set; }

        void SetMotionResDatas(byte[] setBuf, byte AMSNum, byte readNum)
        {
            // 定义初始变量
            float meters = 0;
            byte flagx = 0x02;

            // 判断 readNum 是否有效
            if (readNum != 0xFF)
            {
                if (BambuBusAddress == 0x700) // AMS08
                {
                    meters = -FlashSaveStruct.Instance.Filament[AMSNum, readNum].Meters;
                }
                else if (BambuBusAddress == 0x1200) // AMS Lite
                {
                    meters = FlashSaveStruct.Instance.Filament[AMSNum, readNum].Meters;
                }
            }

            // 填充 setBuf 数组
            setBuf[0] = AMSNum;
            setBuf[2] = flagx;
            setBuf[3] = readNum;

            // 将 float 类型的 meters 转换为字节并写入 setBuf
            byte[] metersBytes = BitConverter.GetBytes(meters);
            Array.Copy(metersBytes, 0, setBuf, 4, metersBytes.Length);

            setBuf[13] = 0;
            setBuf[24] = GetFilamentLeftChar(AMSNum);
        }

        byte GetFilamentLeftChar(byte AMSNum)
        {
            byte data = 0;

            for (int i = 0; i < 4; i++)
            {
                if (FlashSaveStruct.Instance.Filament[AMSNum, i].Status == FilamentStatus.Online)
                {
                    // 1 << (2 * i)
                    data |= (byte)((1 << i) << i);

                    if (FlashSaveStruct.Instance.Filament[AMSNum, i].MotionSet != FilamentMotionState.Idle)
                    {
                        // 2 << (2 * i)
                        data |= (byte)((2 << i) << i);
                    }
                }
            }

            return data;
        }

        bool SetMotion(byte AMSNum, byte readNum, byte statuFlags, byte filamentMotionFlag)
        {
            if (BambuBusAddress == 0x1200) // AMS lite
            {
                if (readNum < 4)
                {
                    if (statuFlags == 0x03 && filamentMotionFlag == 0x3F) // 03 3F
                    {
                        FlashSaveStruct.Instance.BambuBusNowFilamentNum = AMSNum * 4 + readNum;
                        FlashSaveStruct.Instance.Filament[AMSNum, readNum].MotionSet = FilamentMotionState.NeedPullBack;
                    }
                    else if (statuFlags == 0x03 && filamentMotionFlag == 0xBF) // 03 BF
                    {
                        FlashSaveStruct.Instance.BambuBusNowFilamentNum = AMSNum * 4 + readNum;
                        FlashSaveStruct.Instance.Filament[AMSNum, readNum].MotionSet = FilamentMotionState.NeedSendOut;
                    }
                    else
                    {
                        if (FlashSaveStruct.Instance.Filament[AMSNum, readNum].MotionSet ==
                            FilamentMotionState.NeedPullBack)
                            FlashSaveStruct.Instance.Filament[AMSNum, readNum].MotionSet = FilamentMotionState.Idle;
                        else if (FlashSaveStruct.Instance.Filament[AMSNum, readNum].MotionSet ==
                                 FilamentMotionState.NeedSendOut)
                            FlashSaveStruct.Instance.Filament[AMSNum, readNum].MotionSet = FilamentMotionState.OnUse;
                    }
                }
                else if (readNum == 0xFF)
                {
                    for (int i = 0; i < 4; i++)
                    {
                        FlashSaveStruct.Instance.Filament[AMSNum, i].MotionSet = FilamentMotionState.Idle;
                    }
                }
            }
            else if (BambuBusAddress == 0x00) // none
            {
                if (readNum != 0xFF && readNum < 4)
                {
                    FlashSaveStruct.Instance.BambuBusNowFilamentNum = AMSNum * 4 + readNum;
                    FlashSaveStruct.Instance.Filament[AMSNum, readNum].MotionSet = FilamentMotionState.OnUse;
                }
            }
            else
            {
                return false;
            }

            return true;
        }

        public override string ToString()
        {
            return
                $"Frame Type: {(IsShortFrame ? "Short" : "Long")}, Flags: {Flags:X}, CRC8: {Crc8}, CRC16: {Crc16}, Sequence: {Sequence:X}, Length: {Length}, Package: {Package}";
        }
    }
}